// This file is a part of Funky C++.
// Copyright (C) 2011  Valentin David
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
#ifndef _FUNKY_LOOP_CLOCK_HH
# define _FUNKY_LOOP_CLOCK_HH

# include <bitset>
# include "signal.hh"

namespace funky {

  template < typename Control>
  struct loop_clock_sig {
  private:
    Control control;
    size_t position;
    bool started;
    size_t period;
    std::bitset<3> flags;

  public:
    template <typename C>
    explicit loop_clock_sig(C&& c)
      : control(std::forward<C>(c)),
	position(0),
	started(false),
	period(0) {
      flags = *control;
    }

    loop_clock_sig(const loop_clock_sig&) = default;
    loop_clock_sig(loop_clock_sig& other):
      control(other.control),
      position(other.position),
      started(other.started),
      period(other.period),
      flags(other.flags) {}

    loop_clock_sig(loop_clock_sig&& other):
      control(std::move(other.control)),
      position(other.position),
      started(other.started),
      period(other.period),
      flags(std::move(other.flags)) {
    }

    loop_clock_sig& operator=(const loop_clock_sig& other) {
      return *this = loop_clock_sig(other);
    }

    loop_clock_sig& operator=(loop_clock_sig&& other) {
      std::swap(control, other.control);
      std::swap(position, other.position);
      std::swap(started, other.started);
      std::swap(period, other.period);
      std::swap(flags, other.flags);
      return *this;
    }

    std::bitset<3> operator*() const {
      return flags;
    }

    loop_clock_sig& operator++() {
      ++control;
      ++position;
      flags = *control;
      if (period != 0) {
	position = position % period;
	if (position == 0)
	  flags.set(2);
      }
      else {
	if (!started) {
	  if (flags[2]) {
	    std::cerr << "Starting loop: " << position << std::endl;
	    started = true;
	    position = 0;
	  }
	}
	else {
	  if (flags[2]) {
	    std::cerr << "Detected loop: " << position << std::endl;
	    period = position;
	    position = 0;
	  }
	}
      }
      return *this;
    }

    loop_clock_sig& operator++(int) {
      loop_clock_sig ret{*this};
      ++*this;
      return ret;
    }
  };

  template <typename Control>
  struct signal_traits<loop_clock_sig<Control> > {
    typedef size_t duration;
    typedef std::bitset<3> sample;
  };

  template <typename C,
	    typename Control = typename std::decay<C>::type>
  loop_clock_sig<Control>
  loop_clock(C&& c) {
    return loop_clock_sig<Control>{
      std::forward<C>(c)
    };
  }

}

#endif
